﻿using System;
using FakeItEasy;
using NJabber.Stream.Elements;
using NUnit.Framework;

namespace NJabber.Client
{
    [TestFixture]
    public class GenericStanzaHandlerTests
    {
        private GenericStanzaHandler handler;

        [SetUp]
        public void SetUp()
        {
            handler = new GenericStanzaHandler();
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void PrecessStanza_StanzaValueNull_ThrowsException()
        {
            GenericStanzaHandler handler = new GenericStanzaHandler();
            handler.ProcessStanza(A.Dummy<StandartClientRuntime>(), null);
        }

        [Test]
        public void ProcessStanza_IqStanza_UsesIqStanzaHandler()
        {
            IStanzaHandler<IqStanza> iqHandler = CreateFakeHandler<IqStanza>();
            IqStanza stanza = new IqStanza("some-type");
            handler.IqHandlers.Add(iqHandler);
            VerifyHandlerCall(handler, iqHandler, stanza);
        }

        [Test]
        public void ProcessStanza_MessageStanza_UsesMessageStanzaHandler()
        {
            IStanzaHandler<MessageStanza> messageHandler = CreateFakeHandler<MessageStanza>();
            MessageStanza stanza = new MessageStanza();
            handler.MessageHandlers.Add(messageHandler);
            VerifyHandlerCall(handler, messageHandler, stanza);
        }

        [Test]
        public void ProcessStanza_PresenceStanza_UsesPresenceHandler()
        {
            IStanzaHandler<PresenceStanza> presenceHandler = CreateFakeHandler<PresenceStanza>();
            PresenceStanza stanza = new PresenceStanza("some-presence");
            handler.PresenceHandlers.Add(presenceHandler);
            VerifyHandlerCall(handler, presenceHandler, stanza);
        }

        [Test]
        public void ProcessStanza_NotStandartStanzaType_DontUsesAnyHandlerAndReturnsTrue()
        {
            var iqHandler = CreateFakeHandler<IqStanza>();
            var messageHandler = CreateFakeHandler<MessageStanza>();
            var presenceHandler = CreateFakeHandler<PresenceStanza>();
            Stanza stanza = A.Fake<Stanza>();

            bool result = handler.ProcessStanza(A.Dummy<StandartClientRuntime>(), stanza);

            Assert.That(result, Is.True);
            VerifyHandlerNeverCalled(iqHandler);
            VerifyHandlerNeverCalled(messageHandler);
            VerifyHandlerNeverCalled(presenceHandler);
        }

        private void VerifyHandlerNeverCalled<T>(IStanzaHandler<T> handler) where T : Stanza
        {
            A.CallTo(() => handler.ProcessStanza(A.Dummy<StandartClientRuntime>(), A<T>._)).MustNotHaveHappened();
        }

        private IStanzaHandler<T> CreateFakeHandler<T>() where T : Stanza
        {
            return A.Fake<IStanzaHandler<T>>();
        }

        private void VerifyHandlerCall<T>(IStanzaHandler handler, IStanzaHandler<T> childHandler, T stanza) where T : Stanza
        {
            handler.ProcessStanza(A.Dummy<StandartClientRuntime>(), stanza);
            A.CallTo(() => childHandler.ProcessStanza(A<StandartClientRuntime>._, stanza)).MustHaveHappened();
        }
    }
}
